#include "Game.h"

#include "..\SGD Wrappers\CSGD_Direct3D.h"
#include "..\SGD Wrappers\CSGD_DirectInput.h"
#include "..\SGD Wrappers\CSGD_TextureManager.h"
#include "..\SGD Wrappers\CSGD_XAudio2.h"

#include "IGameState.h"
#include "GameplayState.h"
#include "MainMenuState.h"
#include "HowToPlayState.h"
#include "OptionsState.h"
#include "CreditsState.h"
#include "MenuManager.h"

/*static*/ CGame* CGame::GetInstance( void )
{
	// Static local variable (not thread-safe):
	static CGame s_Instance;

	// Return the address
	return &s_Instance;
}


// Constructor
CGame::CGame( void )
{

	// Set the data members to 'clean' values
	m_pD3D			= nullptr;
	m_pDI			= nullptr;
	m_pTM			= nullptr;
	m_pXA			= nullptr;


	m_nScreenWidth	= 0;
	m_nScreenHeight	= 0;
	m_bIsWindowed	= true;


	m_pCurrState	= nullptr;
	m_dwCurrTime	= 0;
}


CGame::~CGame( void )
{
}

void CGame::Initialize( HWND hWnd, HINSTANCE hInstance,
					int nScreenWidth, int nScreenHeight,
					bool bIsWindowed)
{
	m_nScreenWidth	= nScreenWidth;
	m_nScreenHeight	= nScreenHeight;
	m_bIsWindowed	= bIsWindowed;

	// Access the SGD Wrapper singletons
	m_pD3D			= CSGD_Direct3D::GetInstance();
	m_pDI			= CSGD_DirectInput::GetInstance();
	m_pTM			= CSGD_TextureManager::GetInstance();
	m_pXA			= CSGD_XAudio2::GetInstance();

	// Set up the wrappers
	m_pD3D->InitDirect3D( hWnd, m_nScreenWidth, m_nScreenHeight,
						  m_bIsWindowed, true );
	m_pDI->InitDirectInput( hWnd, hInstance, DI_KEYBOARD | DI_MOUSE );
	m_pTM->InitTextureManager( m_pD3D->GetDirect3DDevice(), m_pD3D->GetSprite() );
	m_pXA->InitXAudio2();

	CMenuManager::GetInstance()->AddMenu(CMainMenuState::GetInstance());
	// Start in the gameplay state
	ChangeState( CMenuManager::GetInstance()->PlayBack() );

	//GetTickCount is pretty good for this
	m_dwCurrTime = GetTickCount();
}

bool CGame::Main( void )
{
	// Close the game if the current state does not exist
	//if( m_pCurrState == nullptr )
	//	return false;

	// Handle input
	if( Input() == false )
		return false;


	// Double-check ...
	if( m_pCurrState == nullptr )
		return false;

	// Update game tokens
	Update( );
	

	// Double-check ...
	if( m_pCurrState == nullptr )
		return false;

	// Render the game
	Render();

	return true;
}

void CGame::Shutdown( void )
{
	// Cleanup the current state

	// Shutdown the wrappers
	m_pXA->ShutdownXAudio2();
	m_pTM->ShutdownTextureManager();
	m_pDI->ShutdownDirectInput();
	m_pD3D->ShutdownDirect3D();
	
	// Reset the data members to 'clean' values
	m_pD3D			= nullptr;
	m_pDI			= nullptr;
	m_pTM			= nullptr;
	m_pXA			= nullptr;
	
	m_nScreenWidth	= 0;
	m_nScreenHeight	= 0;
	m_bIsWindowed	= true;
}

bool CGame::Input( void )
{
	// Refresh the direct input buffer
	m_pDI->ReadDevices();

	if( m_pDI->KeyPressed( DIK_RETURN ) && (m_pDI->KeyDown( DIK_RMENU ) || m_pDI->KeyDown( DIK_LMENU )) )
	{
		m_bIsWindowed = !m_bIsWindowed;
		m_pD3D->ChangeDisplayParam(m_nScreenWidth,m_nScreenHeight,m_bIsWindowed);
		return true;
	}
	// Let the current state handle input
	return CMenuManager::GetInstance()->PlayBack()->Input();
}

void CGame::Update( void )
{
	// Calculate elapsed time
	DWORD dwNow = GetTickCount();
	float fElapsedTime = (dwNow - m_dwCurrTime) / 900.0f;

	m_dwCurrTime = dwNow;

	if(fElapsedTime > 0.125f)
		fElapsedTime = 0.125f;

	// Update the xaudio buffers
	m_pXA->Update();

	// Let the current state update
	CMenuManager::GetInstance()->PlayBack()->Update(fElapsedTime);
}

void CGame::Render( void )
{
	// Clear the background (r,g,b)
	m_pD3D->Clear( 0, 0, 0 );

	// Start D3D rendering
	m_pD3D->DeviceBegin();
	m_pD3D->SpriteBegin();

	// DRAW STUFF...
	{
		// Let the current state render
		CMenuManager::GetInstance()->PlayBack()->Render();
	}

	// End the D3D rendering (draw to buffer)
	m_pD3D->SpriteEnd();
	m_pD3D->DeviceEnd();

	// Render the buffer to the screen
	m_pD3D->Present();
}
 
// State Machine
void CGame::ChangeState( MenuState* pNewState )
{
	// Exit the existing state (if any)
	if( m_pCurrState != nullptr )
		m_pCurrState->Exit();
	
	// Store the parameter
	m_pCurrState = pNewState;
	
	// Enter the new state (if any)
	if( m_pCurrState != nullptr )
		m_pCurrState->Enter();
}